package model;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import model.Node.LastNode;
import model.Node.NodeState;

public class PathFinder implements Runnable {

	Thread aStarThread = new Thread(this, "aStar-Thread");

	List<Node> openList = new ArrayList<Node>();
	List<Node> closedList = new ArrayList<Node>();
	List<LastNode> finalPath = new ArrayList<LastNode>();

	int startX = 0;
	int startY = 0;
	int endX = 1;
	int endY = 1;

	@Override
	public void run() {
		findStartAndEndPoint();
		findPath();
		drawPath();
	}

	private void findPath() {

		openList.add(PV.getNodeNet()[startX][startY]);
		int counter = 0;

		int x;
		int y;
		System.out.println("Start: " + startX + " / " + startY);
		System.out.println("End: " + endX + " / " + endY);
		/* ***************************************************************************************** */
		do {
			counter++;
			System.out.println(openList.isEmpty());
			if (openList.isEmpty() && counter != 0)
				break;

			Collections.sort(openList);

			// new to Check Node
			x = openList.get(0).getX();
			y = openList.get(0).getY();

			closedList.add(openList.get(0));
			// openList.get(0).setState(NodeState.CLOSEDLIST);
			openList.remove(0);

			checkNode(x, y, -1, 0, LastNode.EAST); // WEST
			checkNode(x, y, 0, -1, LastNode.SOUTH); // NORTH
			checkNode(x, y, 0, 1, LastNode.NORTH); // SOUTH
			checkNode(x, y, 1, 0, LastNode.WEST); // EAST

			checkNode(x, y, 1, -1, LastNode.SOUTHWEST); // NORTHEAST
			checkNode(x, y, -1, -1, LastNode.SOUTHEAST); // NORTHWEST
			checkNode(x, y, 1, 1, LastNode.NORTHWEST); // SOUTHEAST
			checkNode(x, y, -1, 1, LastNode.NORTHEAST); // SOUTWEST

			System.out.println("run #" + counter);
		} while (!(x == endX && y == endY) && counter <= 9000);

		/* ***************************************************************************************** */

		int newX = endX;
		int newY = endY;
		do {
			Node node = null;

			// findNodeAt newX/newY
			// PublicVars.getMap()[newX][newY] = 'P';
			// PublicVars.getHeatMap()[newX][newY] = 1;
			if (isInOpenList(newX, newY)) {
				System.out.println("openlist");
				node = findNodeInOpenList(newX, newY);
			} else if (isInClosedList(newX, newY)) {
				System.out.println("closedlist");
				node = findNodeInClosedList(newX, newY);
			}
			System.out.println(node);
			// System.out.println(node.getLast());
			System.out.println(node.getLast());
			finalPath.add(node.getLast());
			switch (node.getLast()) {
			case NORTH:
				newY--;
				break;
			case NORTHEAST:
				newY--;
				newX++;
				break;
			case NORTHWEST:
				newY--;
				newX--;
				break;
			case SOUTH:
				newY++;
				break;
			case SOUTHEAST:
				newY++;
				newX++;
				break;
			case SOUTHWEST:
				newY++;
				newX--;
				break;
			case EAST:
				newX++;
				break;
			case WEST:
				newX--;
				break;

			default:
				newX = PV.getNodeNet()[startX][startY].getX();
				newY = PV.getNodeNet()[startX][startY].getY();
				break;
			}

			// System.out.println("loop2");
		} while (!(PV.getNodeNet()[startX][startY].getX() == newX && PV.getNodeNet()[startX][startY].getY() == newY));
		System.out.println(finalPath);
	}

	private void checkNode(int x, int y, int oX, int oY, LastNode last) {

		if (isOnSomething(x + oX, y + oY) || isInClosedList(x + oX, y + oY)) {
			// doNothing!

		} else if (!isInOpenList(x + oX, y + oY)) {
			// Node d = new Node(x - 1, y, 'r', new Point(endX, endY));

			Node d = PV.getNodeNet()[x + oX][y + oY];
			d.setLast(last);
			// d.setState(NodeState.OPENLIST);
			if (isInClosedList(x, y)) {
				d.setgCosts(findNodeInClosedList(x, y).getgCosts() + 1);
			} else if (isInOpenList(x, y)) {
				d.setgCosts(findNodeInOpenList(x, y).getgCosts() + 1);
			}
			// System.out.println(d.getgCost() + "  " + d.getHeuristic());
			openList.add(d);
		}
	}

	private boolean isOnSomething(int x, int y) {

		boolean b = false;
		if (PV.getFieldWidth() <= x || PV.getFieldHeight() <= y || x < 0 || y < 0) {
			b = true;
		} else if (PV.getNodeNet()[x][y].isObstacle()) {
			b = true;
		}
		return b;
	}

	private boolean isInClosedList(int x, int y) {
		boolean b = false;
		start: for (int i = 0; i < closedList.size(); i++) {
			if (closedList.get(i).getX() == x && closedList.get(i).getY() == y) {
				b = true;
				break start;
			}
		}
		return b;
	}

	private Node findNodeInClosedList(int x, int y) {
		int found = 0;
		for (int i = 0; i < closedList.size(); i++) {
			if (closedList.get(i).getX() == x && closedList.get(i).getY() == y) {
				found = i;
			}
		}
		System.out.println(found);
		System.out.println(closedList.get(found));
		// System.out.println(closedList.get(found).getLastNode());
		return closedList.get(found);
	}

	private Node findNodeInOpenList(int x, int y) {
		int found = 0;
		for (int i = 0; i < openList.size(); i++) {
			if (openList.get(i).getX() == x && openList.get(i).getY() == y) {
				found = i;
			}
		}
		// System.out.println(closedList.get(found).getLastNode());
		return openList.get(found);
	}

	private boolean isInOpenList(int x, int y) {
		boolean b = false;

		start: for (int i = 0; i < openList.size(); i++) {
			if (openList.get(i).getX() == x && openList.get(i).getY() == y) {
				b = true;
				break start;
			}
		}
		return b;
	}

	private void findStartAndEndPoint() {
		// TODO Auto-generated method stub

		for (int i = 0; i < PV.getNodeNet().length; i++) {
			for (int j = 0; j < PV.getNodeNet()[i].length; j++) {
				if (PV.getNodeNet()[i][j].isStart()) {
					startX = i;
					startY = j;
				}
				if (PV.getNodeNet()[i][j].isTarget()) {
					endX = i;
					endY = j;
				}
			}
		}

	}

	private void drawPath() {
		int thisX = endX;
		int thisY = endY;
		for (int i = 0; i < finalPath.size(); i++) {
			switch (finalPath.get(i)) {
			case NORTH:
				thisY--;
				break;
			case NORTHEAST:
				thisY--;
				thisX++;
				break;
			case NORTHWEST:
				thisY--;
				thisX--;
				break;
			case SOUTH:
				thisY++;
				break;
			case SOUTHEAST:
				thisY++;
				thisX++;
				break;
			case SOUTHWEST:
				thisY++;
				thisX--;
				break;
			case EAST:
				thisX++;
				break;
			case WEST:
				thisX--;
				break;
			}
			PV.getNodeNet()[thisX][thisY].setState(NodeState.PATH);
			if(thisX == endX && thisY == endY){
				break;
			}
		}
		
	}

	public Thread getaStarThread() {
		return aStarThread;
	}

}
